/*---------------------------------------------------------------------
MdmpParser.cpp - A minimal parser for user mode dump file.  
Software Debugging by Raymond Zhang, All rights reserved. 
---------------------------------------------------------------------*/

#include "stdafx.h"
#include "UdmpView.h"
#include "MdmpParser.h"
#include "dbghelp.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
static	LPCTSTR szStreamTypes[]={
		"UnusedStream",                //= 0,
		"ReservedStream0",
		"ReservedStream1",
		"ThreadListStream",
		"ModuleListStream",
		"MemoryListStream",
		"ExceptionStream",
		"SystemInfoStream",
		"ThreadExListStream",
		"Memory64ListStream",
		"CommentStreamA",
		"CommentStreamW",
		"HandleDataStream",
		"FunctionTableStream",
		"UnloadedModuleListStream",
		"MiscInfoStream",
		"MemoryInfoListStream",
		"ThreadInfoListStream",
		"HandleOperationListStream"
		//LastReservedStream          = 0xffff
		};

CMdmpParser::CMdmpParser()
{
	m_hWndListener=NULL;
	m_hDumpFile=INVALID_HANDLE_VALUE;
	m_hDumpMapFile=INVALID_HANDLE_VALUE;
	m_pBaseofDump=NULL;
	m_ulFileSize=0;
}

CMdmpParser::~CMdmpParser()
{
	if(m_hDumpFile!=INVALID_HANDLE_VALUE)
		Close();
}
//----------------------------------------------------------------------
//
// DbgMsg
//
// Output debug information to a list box or a console which can receive
// debug information generated by OutputDebugString API. 
//
//----------------------------------------------------------------------
#define MAX_MESSAGE_SIZE 1024
void CMdmpParser::OutMsg(LPCTSTR szMsgFormat,...)
{
	TCHAR szMessage[MAX_MESSAGE_SIZE];

    va_list va;
    va_start( va, szMsgFormat );

    int nReturnValue = _vsntprintf( szMessage, 
		MAX_MESSAGE_SIZE, szMsgFormat, va );

    va_end(va);

	if(m_hWndListener)
		SendMessage(m_hWndListener,
			LB_ADDSTRING,NULL,(LPARAM)szMessage);

#ifdef ADV_DBG
	OutputDebugString(szMessage);
#endif
}

HRESULT CMdmpParser::Open(LPCTSTR lpszFileName,HWND hWndStreamList)
{
	PMINIDUMP_HEADER pMdpHeader;
	char szSignature[sizeof(ULONG32)+1];

	m_hDumpFile=CreateFile(lpszFileName, 
		GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 
	if(m_hDumpFile==INVALID_HANDLE_VALUE) 
	{
		OutMsg(_T("Failed to CreateFile(%s) for 0x%x"),
			lpszFileName,GetLastError()); 
		return E_FAIL;     
	}

	m_hDumpMapFile=CreateFileMapping(m_hDumpFile, 
		NULL, PAGE_READONLY, 0, 0, NULL); 
	if(m_hDumpMapFile==NULL) 
	{
		OutMsg(_T("Failed to CreateFileMapping(0x%x) for 0x%x"), 
			m_hDumpFile,GetLastError()); 
		return E_FAIL;     
	}

	m_pBaseofDump=MapViewOfFile(m_hDumpMapFile, FILE_MAP_READ, 0, 0, 0); 
	if(m_pBaseofDump==NULL) 
	{
		OutMsg( _T("Failed to MapViewOfFile(0x%x) for 0x%x"), 
			m_hDumpMapFile,GetLastError() ); 
		return E_FAIL;     
	}
	pMdpHeader=(PMINIDUMP_HEADER)m_pBaseofDump;
	if(pMdpHeader->Signature!=MINIDUMP_SIGNATURE)
	{
		OutMsg(_T("File [%s] is not a valid user dump [0x%x]."),
			lpszFileName,pMdpHeader->Signature);
	}
	GetFileSizeEx(m_hDumpFile,(PLARGE_INTEGER)&m_ulFileSize);
	OutMsg(_T("File [%s] is mapped to 0x%x successfuly, size=%I64u"),
		lpszFileName,m_pBaseofDump,m_ulFileSize);
	strncpy(szSignature,(const char *)&(pMdpHeader->Signature),sizeof(ULONG32));
	szSignature[sizeof(ULONG32)]=0;
	OutMsg(_T("Header: Signature=%s(0x%x), Version=0x%x, Streams=%u,DirRva=0x%x"),
		szSignature,pMdpHeader->Signature,
		pMdpHeader->Version,
		pMdpHeader->NumberOfStreams,
		pMdpHeader->StreamDirectoryRva);
	OutMsg(_T("Header: CheckSum=0x%x, Stamp=0x%x, Flags=0x%x"),
		pMdpHeader->CheckSum,
		pMdpHeader->TimeDateStamp,
		pMdpHeader->Flags);
	this->FillStreamList(hWndStreamList);
	return S_OK;
}

void CMdmpParser::SetListener(HWND hWndListBox)
{
	m_hWndListener=hWndListBox;
}

void CMdmpParser::Close()
{
	if(m_hDumpFile==INVALID_HANDLE_VALUE)
	{
		OutMsg("No file is open");
		return;
	}
	if(m_pBaseofDump!=NULL)
	{
		m_pBaseofDump=INVALID_HANDLE_VALUE;
	}
	if(m_hDumpMapFile!=INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hDumpMapFile);
		m_hDumpMapFile=INVALID_HANDLE_VALUE;
	}
	CloseHandle(m_hDumpFile);
	m_hDumpFile=INVALID_HANDLE_VALUE;
}

/*void CMdmpParser::FillStreamList(HWND hListStream)
{
	SendMessage(hListStream,LB_RESETCONTENT,0,0);
	for(int i=0;i<sizeof(szStreamTypes)/sizeof(szStreamTypes[0]);i++)
	{
		SendMessage(hListStream,LB_INSERTSTRING,-1,(LPARAM)szStreamTypes[i]);
	}
}*/
void CMdmpParser::FillStreamList(HWND hListStream)
{
	PMINIDUMP_HEADER pMdpHeader;
	PMINIDUMP_DIRECTORY pDirEntry=0; 
	TCHAR szMsg[MAX_PATH];
	RECT rc;
	UINT nIndex;

	GetWindowRect(hListStream,&rc);
	SendMessage(hListStream,LB_RESETCONTENT,0,0);
	SendMessage(hListStream,LB_SETCOLUMNWIDTH, 
		(rc.right-rc.left)/2,0-8);

	pMdpHeader=(PMINIDUMP_HEADER)m_pBaseofDump;

	pDirEntry= (PMINIDUMP_DIRECTORY)((ULONG)m_pBaseofDump+
		pMdpHeader->StreamDirectoryRva);

	for(ULONG i=0;i<pMdpHeader->NumberOfStreams;i++)
	{
		sprintf(szMsg,_T("%s(%d) [0x%x] at 0x%x"),
			GetStreamType(pDirEntry->StreamType),
			pDirEntry->StreamType,
			pDirEntry->Location.DataSize,
			pDirEntry->Location.Rva);
		nIndex=SendMessage(hListStream,LB_INSERTSTRING,-1,
			(LPARAM)szMsg);
		SendMessage(hListStream,LB_SETITEMDATA,nIndex,
			pDirEntry->StreamType);

		pDirEntry++;
	}
}

HRESULT CMdmpParser::ReadStreams(HWND hLBStreamTypes)
{
	int nStreamType=0;

	if(!IsOpen())
	{
		OutMsg(_T("Please open a file firstly"));
		return E_FAIL;
	}
	int nTotal=SendMessage(hLBStreamTypes,LB_GETCOUNT,0,0);
	for(int i=0;i<nTotal;i++)
	{
		if(SendMessage(hLBStreamTypes,LB_GETSEL,i,0))
		{
			nStreamType=SendMessage(hLBStreamTypes,LB_GETITEMDATA,i,0);
			ReadStream(nStreamType);
		}
	}
	return S_OK;
}

HRESULT CMdmpParser::ReadStream(ULONG nStreamType)
{
	HRESULT hRet=E_FAIL;
	PMINIDUMP_DIRECTORY pMdpDir=0; 
	PVOID pStream=0; 
	ULONG ulStreamSize=0; 

	if(!MiniDumpReadDumpStream(m_pBaseofDump,nStreamType,
		&pMdpDir, &pStream, &ulStreamSize)) 
	{
		if(GetLastError()==0)
		{
			OutMsg(_T("Stream [%s-%u] does not exist"),
				GetStreamType(nStreamType),nStreamType);
			return E_FAIL;
		}
		OutMsg(_T("MiniDumpReadDumpStream(0x%x,%u,...) returned failure 0x%x"), 
			m_pBaseofDump, nStreamType, GetLastError()); 
		return E_FAIL;
	}
	OutMsg(_T("Got stream[%s] at 0x%x, base=0x%x, dir=0x%x, size=0x%x(%u)"),
		GetStreamType(nStreamType),pStream,m_pBaseofDump,pMdpDir,
		ulStreamSize,ulStreamSize);

	if((pStream==NULL)||(ulStreamSize==0)) 
	{
		OutMsg( _T("Invalid stream type %u"), nStreamType); 
		return E_FAIL;
	}
	OutMsg(_T("DIR: StreamType=%u, DataSize=%u, RVA=0x%x(%u)"),
		pMdpDir->StreamType,
		pMdpDir->Location.DataSize,
		pMdpDir->Location.Rva,
		pMdpDir->Location.Rva);

	switch(nStreamType)
	{
    case ReservedStream0://             = 1,
		break;
    case ReservedStream1://             = 2,
		break;
    case ThreadListStream://            = 3,
		hRet=this->ShowThreadList((PMINIDUMP_THREAD_LIST)pStream,ulStreamSize);
		break;
    case ModuleListStream://            = 4,
		hRet=this->ShowModuleList((PMINIDUMP_MODULE_LIST)pStream,ulStreamSize);
		break;
    case MemoryListStream://            = 5,
		hRet=this->ShowMemoryList((PMINIDUMP_MEMORY_LIST)pStream,ulStreamSize);
		break;
    case ExceptionStream://             = 6,
		hRet=this->ShowException((PMINIDUMP_EXCEPTION_STREAM)pStream,ulStreamSize);
		break;
    case SystemInfoStream://            = 7,
		hRet=this->ShowSysInfo((PMINIDUMP_SYSTEM_INFO)pStream,ulStreamSize);
		break;
    case ThreadExListStream://          = 8,
		hRet=this->ShowThreadExList((PMINIDUMP_THREAD_EX_LIST)pStream,ulStreamSize);
		break;
    case Memory64ListStream://          = 9,
		break;
    case CommentStreamA://              = 10,
		OutMsg(_T("%s"),(char*)pStream);
		break;
    case CommentStreamW://              = 11,
		OutMsg(_T("%ws"),(WCHAR*)pStream);
		break;
    case HandleDataStream://            = 12,
		break;
    case FunctionTableStream://         = 13,
		break;
    case LastReservedStream: //          = 0xffff
		break;
	}
	return hRet;
}

LPCTSTR CMdmpParser::GetStreamType(ULONG nStreamType)
{
	if(nStreamType>=0 && 
		nStreamType<sizeof(szStreamTypes)/sizeof(szStreamTypes[0]))
		return szStreamTypes[nStreamType];
	return _T("UserStream");
}

HRESULT CMdmpParser::ShowThreadList(
			PMINIDUMP_THREAD_LIST pThreadListStream,ULONG ulStreamSize)
{
	ULONG nThreads=pThreadListStream->NumberOfThreads;
	OutMsg(_T("Total %u thread(s) info at 0x%x"),
		nThreads,pThreadListStream);
	for(ULONG i=0;i<nThreads;i++)
	{
		OutMsg(_T("Thread %u: ID=%u, SuspendCount=%u, PrvClass=%u, Priority=%u, TEB=0x%I64x"),i,
			pThreadListStream->Threads[i].ThreadId,
			pThreadListStream->Threads[i].SuspendCount,
			pThreadListStream->Threads[i].PriorityClass,
			pThreadListStream->Threads[i].Priority,
			pThreadListStream->Threads[i].Teb);
		OutMsg(_T("Stack Length: 0x%I64x(%I64u)"),
			pThreadListStream->Threads[i].Stack.StartOfMemoryRange,
			pThreadListStream->Threads[i].Stack.StartOfMemoryRange);
		ShowLocation(_T("Stack"),&(pThreadListStream->Threads[i].Stack.Memory));
		ShowLocation(_T("ThreadContext"),&(pThreadListStream->Threads[i].ThreadContext));
	}
	return S_OK;
}

HRESULT CMdmpParser::ShowModuleList(
			PMINIDUMP_MODULE_LIST pModuleListStream,ULONG ulStreamSize)
{
	PMINIDUMP_STRING pDmpString;
	ULONG nModules=pModuleListStream->NumberOfModules;
	OutMsg(_T("Total %u module(s) info at 0x%x"),
		nModules,pModuleListStream);
	for(ULONG i=0;i<nModules;i++)
	{
		OutMsg(_T("Module %u: Base=0x%I64x, Size=%u, CheckSum=%u, Stamp=%u"),i,
			pModuleListStream->Modules[i].BaseOfImage,
			pModuleListStream->Modules[i].SizeOfImage,
			pModuleListStream->Modules[i].CheckSum,
			pModuleListStream->Modules[i].TimeDateStamp);
		pDmpString=(PMINIDUMP_STRING)((ULONG)this->m_pBaseofDump+
			pModuleListStream->Modules[i].ModuleNameRva);
		OutMsg(_T("Name: RVA=0x%x, Length=%u - %ws"), 
			pModuleListStream->Modules[i].ModuleNameRva,
			pDmpString->Length,pDmpString->Buffer);
		OutMsg(_T("Version: Signature=0x%x, StrucVersion=%u, FileVerMS=%u, FileVersionLS=%u"),
			pModuleListStream->Modules[i].VersionInfo.dwSignature,
			pModuleListStream->Modules[i].VersionInfo.dwStrucVersion,
			pModuleListStream->Modules[i].VersionInfo.dwFileVersionMS,
			pModuleListStream->Modules[i].VersionInfo.dwFileVersionLS
			);
		OutMsg(_T("FileFlagsMask=0x%x, FileFlags=%u, FileOS=%u, FileType=%u"),
			pModuleListStream->Modules[i].VersionInfo.dwFileFlagsMask,
			pModuleListStream->Modules[i].VersionInfo.dwFileFlags,
			pModuleListStream->Modules[i].VersionInfo.dwFileOS,
			pModuleListStream->Modules[i].VersionInfo.dwFileType
			);
		OutMsg(_T("FileDateMS=0x%x, FileFateLS=0x%x"),
			pModuleListStream->Modules[i].VersionInfo.dwFileDateMS,
			pModuleListStream->Modules[i].VersionInfo.dwFileDateLS
			);
		ShowLocation(_T("CvRecord"),&(pModuleListStream->Modules[i].CvRecord));
		ShowLocation(_T("MiscRecord"),&(pModuleListStream->Modules[i].MiscRecord));
		OutMsg(_T("Reserved0=0x%I64x, Reserved1=0x%I64x"),
			pModuleListStream->Modules[i].Reserved0,
			pModuleListStream->Modules[i].Reserved1);
	}
	return S_OK;
}

HRESULT CMdmpParser::ShowMemoryList(
			PMINIDUMP_MEMORY_LIST pMemoryStream,ULONG ulStreamSize)
{
	ULONG nMemRanges=pMemoryStream->NumberOfMemoryRanges;
	for(ULONG i=0;i<nMemRanges;i++)
	{
		OutMsg(_T("Memory Range[%u]: Start=0x%I64x"),
			i,pMemoryStream->MemoryRanges[i].StartOfMemoryRange);
		ShowLocation(_T("- Memory"), &(pMemoryStream->MemoryRanges[i].Memory));
	}
	return S_OK;
}

HRESULT CMdmpParser::ShowException(
			PMINIDUMP_EXCEPTION_STREAM pExceptionStream,ULONG ulStreamSize)
{
	OutMsg(_T("Exception: ThreadId=%u, alignment=%u"),
		pExceptionStream->ThreadId,pExceptionStream->__alignment);
	OutMsg(_T("ExceptionRecord: Code=0x%x, Flags=0x%x, Associated=0x%I64x, Address=0x%I64x"),
		pExceptionStream->ExceptionRecord.ExceptionCode,
		pExceptionStream->ExceptionRecord.ExceptionFlags,
		pExceptionStream->ExceptionRecord.ExceptionRecord,
		pExceptionStream->ExceptionRecord.ExceptionAddress);
	for(ULONG i=0;i<pExceptionStream->ExceptionRecord.NumberParameters;i++)
	{
		OutMsg(_T("Parameters[%u]=0x%x"), i, 
			pExceptionStream->ExceptionRecord.ExceptionInformation[i]);
	}
	ShowLocation(_T("ThreadContext"),&(pExceptionStream->ThreadContext));
	return S_OK;
}

HRESULT CMdmpParser::ShowSysInfo(
			PMINIDUMP_SYSTEM_INFO pSysInfoStream,ULONG ulStreamSize)
{
	OutMsg(_T("Processor: Architecture=%hu, Level=%hu, Revision=%hu, Reserved=%hu"),
		pSysInfoStream->ProcessorArchitecture,
		pSysInfoStream->ProcessorLevel,
		pSysInfoStream->ProcessorRevision,
		pSysInfoStream->Reserved0);
	OutMsg(_T("MajorVersion=%u, MinorVersion=%u, BuildNumber=%u, PlatformId=%u"), 
		pSysInfoStream->MajorVersion,
		pSysInfoStream->MinorVersion,
		pSysInfoStream->BuildNumber,
		pSysInfoStream->PlatformId);
	OutMsg(_T("CSDVersionRva=0x%x - %s"),
		pSysInfoStream->CSDVersionRva,
		(char*)((ULONG)m_pBaseofDump+pSysInfoStream->CSDVersionRva));
	OutMsg(_T("Reserved 1(SuiteMask)=0x%x"),
		// pSysInfoStream->SuiteMask,
		pSysInfoStream->Reserved1);
	OutMsg(_T("CPUID Sub-0: VendorId[0](EAX)=0x%x, VendorId[1](EBX)=0x%x, VendorId[2](ECX)=0x%x"), 
		pSysInfoStream->Cpu.X86CpuInfo.VendorId[0],
		pSysInfoStream->Cpu.X86CpuInfo.VendorId[1],
		pSysInfoStream->Cpu.X86CpuInfo.VendorId[2]);
	OutMsg(_T("Sub-1: VersionInformation(EAX)=0x%x, FeatureInformation(EDX)=0x%x, AMDExtendedCpuFeatures=0x%x"),
		pSysInfoStream->Cpu.X86CpuInfo.VersionInformation,
		pSysInfoStream->Cpu.X86CpuInfo.FeatureInformation,
		pSysInfoStream->Cpu.X86CpuInfo.AMDExtendedCpuFeatures);
	OutMsg(_T("OtherCpuInfo: ProcessorFeatures[0]=0x%x, ProcessorFeatures[1]=0x%x"),
		pSysInfoStream->Cpu.OtherCpuInfo.ProcessorFeatures[0],
		pSysInfoStream->Cpu.OtherCpuInfo.ProcessorFeatures[0]);
		
	return S_OK;
}

HRESULT CMdmpParser::ShowThreadExList(
			PMINIDUMP_THREAD_EX_LIST pThreadExListStream,ULONG ulStreamSize)
{
	return S_OK;
}

void CMdmpParser::ShowLocation(LPCTSTR szObject, 
							   MINIDUMP_LOCATION_DESCRIPTOR *pMemLocation)
{
	//ULONG64 DataSize=pMemLocation;
	//RVA64 Rva;
	if(sizeof(pMemLocation->DataSize)==sizeof(ULONG))
		OutMsg(_T("%s is at 0x%x, size=0x%x"),
			szObject, pMemLocation->Rva, pMemLocation->DataSize);
	else
		OutMsg(_T("%s is at 0x%I64x, size=0x%I64x"),
			szObject, pMemLocation->Rva, pMemLocation->DataSize);
}

HRESULT CMdmpParser::RawRead(ULONG ulRVA, ULONG ulSize)
{
	PULONG pulData=(PULONG)((ULONG)m_pBaseofDump+ulRVA);
	if(ulRVA+ulSize>m_ulFileSize)
	{
		OutMsg(_T("The range (ulRVA+ulSize=%u) is out of file size (%u)"),
			ulRVA+ulSize,m_ulFileSize);
		return E_FAIL;
	}
	ULONG nRows=ulSize/sizeof(ULONG);
	nRows/=4;
	if(ulSize%4)
		nRows+=1;

	for(ULONG i=0;i<nRows;i++)
	{
		OutMsg(_T("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x"),
			pulData,*pulData,*(pulData+1),*(pulData+2),*(pulData+3));
		pulData+=4;
	}
	return S_OK;
}
